package com.kid.keep.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.google.common.collect.Lists;
import com.honeybee.cloud.framework.common.idwork.IdWorker;
import com.kid.keep.bo.CheckItemReportBO;
import com.kid.keep.bo.ChildCheckItemBO;
import com.kid.keep.bo.StatisticsDate;
import com.kid.keep.constant.CheckItemConstant;
import com.kid.keep.dao.child.ChildRepository;
import com.kid.keep.dao.item.CheckItemReportRepository;
import com.kid.keep.dao.item.CheckItemRepository;
import com.kid.keep.dao.item.CheckItemStandardRepository;
import com.kid.keep.dao.item.ChildCheckItemRepository;
import com.kid.keep.dto.AddChildDTO;
import com.kid.keep.dto.CheckItemReportDTO;
import com.kid.keep.dto.ChildDTO;
import com.kid.keep.dto.UpdateChildDTO;
import com.kid.keep.enums.BizExceptionEnum;
import com.kid.keep.enums.DateStatusEnum;
import com.kid.keep.enums.ReportStatusEnum;
import com.kid.keep.exception.BizException;
import com.kid.keep.mapper.ICheckItemCovertMapper;
import com.kid.keep.mapper.ICheckReportCovertMapper;
import com.kid.keep.mapper.IChildCovertMapper;
import com.kid.keep.po.*;
import com.kid.keep.service.IChildService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @Description 小孩 service
 * @Author JOHN
 * @Create 2020-01-03
 * @Since 1.0.0
 */
@Service
@Slf4j
public class ChildService implements IChildService {

    private ChildRepository childRepository;
    private IChildCovertMapper childCovertMapper;
    private CheckItemRepository checkItemRepository;
    private IdWorker idWorker;
    private CheckItemReportRepository checkItemReportRepository;
    private ChildCheckItemRepository childCheckItemRepository;
    private ICheckItemCovertMapper checkItemCovertMapper;
    private ICheckReportCovertMapper checkReportCovertMapper;

    @Autowired
    public ChildService(ChildRepository childRepository, CheckItemRepository checkItemRepository, CheckItemReportRepository checkItemReportRepository, ChildCheckItemRepository childCheckItemRepository) {
        this.childRepository = childRepository;
        this.checkItemRepository = checkItemRepository;
        this.checkItemReportRepository = checkItemReportRepository;
        this.childCheckItemRepository = childCheckItemRepository;
    }

    @Autowired
    public void setCheckReportCovertMapper(ICheckReportCovertMapper checkReportCovertMapper) {
        this.checkReportCovertMapper = checkReportCovertMapper;
    }

    @Autowired
    public void setChildCovertMapper(IChildCovertMapper childCovertMapper) {
        this.childCovertMapper = childCovertMapper;
    }

    @Autowired
    public void setIdWorker(IdWorker idWorker) {
        this.idWorker = idWorker;
    }

    @Autowired
    public void setCheckItemCovertMapper(ICheckItemCovertMapper checkItemCovertMapper) {
        this.checkItemCovertMapper = checkItemCovertMapper;
    }


    @Override
    public ChildDTO addChild(AddChildDTO addChildDTO) {
        Assert.notNull(addChildDTO, "request param is null");
        Assert.notBlank(addChildDTO.getName(), "name is null");
        checkChildName(addChildDTO.getParentId(), addChildDTO.getName(), null);
        Child child = childCovertMapper.toPO(addChildDTO, idWorker.nextId(), new Date(), DateStatusEnum.VALID.getValue());
        childRepository.save(child);
        return childCovertMapper.toDTO(child);
    }


    @Override
    public void updateChild(UpdateChildDTO updateChildDTO, Long parentId) {
        Assert.notNull(updateChildDTO, "request param is null");
        Assert.notNull(updateChildDTO.getId(), "id is null");
        checkChildName(parentId, updateChildDTO.getName(), updateChildDTO.getId());
        Child child = childCovertMapper.toPO(updateChildDTO, new Date());
        int resultNumber = childRepository.updateByIdAndDataStatus(child, child.getId(), DateStatusEnum.VALID.getValue());
        if (resultNumber <= 0) {
            throw BizException.getInstance(BizExceptionEnum.ID_NOT_EXISTS);
        }
    }

    @Override
    public ChildDTO getChildByIdAndParentId(Long childId, Long parentId) {
        Assert.notNull(parentId, "request param is null");
        Assert.notNull(childId, "id is null");
        Child child = childRepository.findByIdAndParentIdAndDataStatus(childId, parentId, DateStatusEnum.VALID.getValue());
        return childCovertMapper.toDTO(child);
    }

    @Override
    public ChildDTO getNewestChildByParentId(Long parentId) {
        Assert.notNull(parentId, "request param is null");
        Child child = childRepository.findNewestChildByParentIdAndDataStatus(parentId, DateStatusEnum.VALID.getValue());
        return childCovertMapper.toDTO(child);
    }

    @Override
    public void deleteChild(Long childId, Long userId) {
        Assert.notNull(childId, "request param is null");
        Assert.notNull(userId, "userId is null");
        checkChildId(childId, userId);
        Child child = childCovertMapper.toPO(childId, userId, DateStatusEnum.INVALID.getValue(), new Date());
        childRepository.updateByIdAndParentId(child, childId, userId);
    }

    @Override
    public List<ChildDTO> listChildByParentId(Long parentId) {
        Assert.notNull(parentId, "userId is null");
        List<Child> childList = childRepository.findAllChildByParentIdAndDataStatus(parentId, DateStatusEnum.VALID.getValue());
        return childCovertMapper.toDTO(childList);
    }

    @Override
    public CheckItemReportDTO openingCheckItemReport(Long childId, Long userId) {
        Assert.notNull(childId, "childId is null");
        Assert.notNull(userId, "userId is null");
        Child child = checkChildId(childId, userId);
        existsCheckItem(childId);
        CheckItemReport checkItemReport = addCheckItem(child, userId);
        return checkItemCovertMapper.toDTO(checkItemReport);
    }

    @Transactional(rollbackForClassName = "Exception")
    @Override
    public CheckItemReportDTO forcedOpeningCheckItemReport(Long childId, Long userId) {
        Assert.notNull(childId, "childId is null");
        Assert.notNull(userId, "userId is null");
        Child child = checkChildId(childId, userId);
        invalidUnReportCheckItem(childId);
        CheckItemReport checkItemReport = addCheckItem(child, userId);
        return checkItemCovertMapper.toDTO(checkItemReport);
    }

    @Override
    public List<CheckItemReportDTO> countCheckReportByDateTypeAndChildId(Long childId, Integer type, Long creatorId) {
        Assert.notNull(childId, "childId is null");
        Assert.notNull(creatorId, "creatorId is null");
        checkChildId(childId, creatorId);
        StatisticsDate statisticsDate = StatisticsDate.getDate(type);
        List<CheckItemReportBO> checkItemReportList = checkItemReportRepository.listByDateAndCreatorIdAndChildIdAndStatus(statisticsDate.getStartDate(), statisticsDate.getEndDate(), childId, creatorId, ReportStatusEnum.REPORTED.getValue());
        return checkReportCovertMapper.toDTO(checkItemReportList);
    }

    /**
     * 作废未完成的测评报告
     *
     * @param childId
     */
    private void invalidUnReportCheckItem(Long childId) {
        CheckItemReport checkItemReport = new CheckItemReport();
        checkItemReport.setStatus(ReportStatusEnum.INVALID.getValue());
        checkItemReport.setUpdateDate(new Date());
        checkItemReportRepository.updateByChildIdAndStatus(checkItemReport, childId, ReportStatusEnum.UNREPORTED.getValue());

    }

    /**
     * 增加评测项
     *
     * @param child
     */
    private CheckItemReport addCheckItem(Child child, Long userId) {
        List<CheckItem> checkItems = checkItemRepository.finAllItem(DateStatusEnum.VALID.getValue());
        if (CollectionUtils.isEmpty(checkItems)) {
            throw BizException.getInstance(BizExceptionEnum.CHECK_ITEM_NOT_EXISTS);
        }
        final Long id = idWorker.nextId();
        CheckItemReport checkItemReport = checkItemCovertMapper.toPO(id, child, DateStatusEnum.VALID.getValue(), new Date(), null, userId);
        if (checkItemReport.getAge() < CheckItemConstant.MIN_AGE || checkItemReport.getAge() > CheckItemConstant.MAX_AGE) {
            throw BizException.getInstance(BizExceptionEnum.AGE_NOT_MATCH);
        }
        final List<ChildCheckItem> childCheckItems = Lists.newArrayList();
        checkItems.stream().forEach(e -> childCheckItems.add(checkItemCovertMapper.toPO(e, id, child.getId(), idWorker.nextId())));
        checkItemReportRepository.save(checkItemReport);
        childCheckItemRepository.saveBatch(childCheckItems);
        return checkItemReport;
    }


    /**
     * 判断是否有15天内未完成的评测
     *
     * @param childId
     */
    private void existsCheckItem(Long childId) {
        CheckItemReportBO existInfo = checkItemReportRepository.findByChildIdAndStatus(childId, DateStatusEnum.VALID.getValue());
        if (Objects.isNull(existInfo)) {
            return;
        }
        long diffDay = Math.abs(DateUtil.betweenDay(existInfo.getCreateDate(), new Date(), true));
        //返回有的测评组Id  15天内的有效
        if (diffDay <= CheckItemConstant.MAX_CHECK_DAY) {
            checkItemScope(existInfo.getId());
           //如果只是开始了一个记录，直接作废掉
            // throw BizException.getInstance(BizExceptionEnum.CHECK_ITEM_EXISTS, Objects.toString(existInfo.getId()));
        }

        //15天以外的直接作废
        CheckItemReport checkItemReport = new CheckItemReport();
        checkItemReport.setId(existInfo.getId());
        checkItemReport.setStatus(ReportStatusEnum.INVALID.getValue());
        checkItemReportRepository.updateById(checkItemReport);
    }

    /**
     * 判断是否有已经测试过的项目
     * @param reportId
     */
    private void checkItemScope(Long reportId) {
        List<ChildCheckItemBO> childCheckItemBOS = childCheckItemRepository.findAllByReportId(reportId);
        if (CollectionUtils.isEmpty(childCheckItemBOS)) {
            return;
        }
        Long count = childCheckItemBOS.stream().filter(e -> Objects.nonNull(e.getScope())).count();
        if (count > 0) {
            throw BizException.getInstance(BizExceptionEnum.EXIST_SCOPE, Objects.toString(reportId));
        }
    }


    /**
     * 判断小孩是不是属于登录用户的
     *
     * @param childId
     * @return
     */
    private Child checkChildId(Long childId, Long userId) {
        Child child = childRepository.findByIdAndParentIdAndDataStatus(childId, userId, DateStatusEnum.VALID.getValue());
        if (Objects.isNull(child)) {
            throw BizException.getInstance(BizExceptionEnum.ID_NOT_EXISTS);
        }
        return child;
    }

    /**
     * 判断小孩名是否存在
     *
     * @param parentId
     * @param name
     */
    private void checkChildName(Long parentId, String name, Long userId) {
        Child existChild = childRepository.findByParentIdAndNameAndDataStatus(parentId, name, DateStatusEnum.VALID.getValue());
        if (Objects.nonNull(existChild) && !Objects.equals(userId, existChild.getId())) {
            throw BizException.getInstance(BizExceptionEnum.NAME_EXISTS);
        }
    }

}
